//
//  QSActivityIndicatorView.swift
//  QSActivityIndicatorView
//
//  Created by 火星人 on 2017/11/2.
//  Copyright © 2017年 火星人. All rights reserved.
//

import UIKit

public enum QSActivityIndicatorViewStartPosition: Int {
    case top = 90
    case left = 0
    case bottom = 270
    case right = 180
}

public class QSActivityIndicatorView: UIView {
    
    public lazy var blockHeight: CGFloat = 4
    public lazy var hollowR: CGFloat = 8
    public lazy var roundBlockStyle = true
    public var  progressTintColor = UIColor.brown {
        didSet{
            for b in blocks {
                b.shapeLayer.fillColor = progressTintColor.cgColor
            }
        }
    }
    fileprivate var p: CGFloat = 0
    public var  progress: CGFloat {
        
        set{
            if newValue < 0 {
                p = 0
            }
            else if newValue > 1 {
                p = 1
            }
            else {
                p = newValue
            }
            didSetProgress()
        }
        get{
            return p
        }
        
    }
    public var  startPosition = QSActivityIndicatorViewStartPosition.top {
        didSet{
            CATransaction.begin()
            CATransaction.setDisableActions(true)
            let angle = CGFloat(rad * Double(startPosition.rawValue))
            parentLayer.transform = CATransform3DMakeRotation(angle, 0, 0, 1)
            CATransaction.commit()
        }
    }
    
    fileprivate lazy var blocks = [BlockLayer]()
    
    fileprivate var displayLink: CADisplayLink!
    fileprivate var task: (() -> ())?
    fileprivate lazy var step = 0
    fileprivate lazy var curStep = 0
    fileprivate lazy var cur = 0
    fileprivate lazy var progressStep: CGFloat = 0
    fileprivate let rad = Double.pi / 180
    fileprivate var spinAngle = -1
    fileprivate var spinTargetAngle = 0
    fileprivate lazy var parentLayer = CALayer()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }
    
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }
    
    private func setup(){
        
        self.displayLink = CADisplayLink(target: self, selector: #selector(displayLinkHandle))
        self.displayLink.add(to: RunLoop.main, forMode: .commonModes)
        self.displayLink.isPaused = true
        
        self.layer.addSublayer(parentLayer)
        resetUI()
    }
      
    public func resetUI() {
        
        if let ls = parentLayer.sublayers {
            for l in ls {
                l.removeFromSuperlayer()
            }
        }
        blocks.removeAll()
        
        let blockWidth = blockHeight * 3.3
        
        let rect = CGRect(x: 0, y: 0, width: blockWidth, height: blockHeight)
        var corner: CGFloat = 0
        if roundBlockStyle {
            corner = blockHeight * 0.5
        }
        let path = CGPath(roundedRect: rect, cornerWidth: corner, cornerHeight: corner, transform: nil)
        
        let w = (blockWidth + hollowR) * 2
        let layerFrame = CGRect(x: 0, y: (w - blockHeight) * 0.5 , width: w, height: blockHeight)
        
        parentLayer.frame = CGRect(x: 0, y: 0, width: w, height: w)
        
        for i in 0 ..< 12 {
            
            let ll = BlockLayer()
            ll.shapeLayer.path = path
            ll.shapeLayer.fillColor = progressTintColor.cgColor
            ll.frame = layerFrame
            ll.anchorPoint = CGPoint(x: 0.5, y: 0.5)
            parentLayer.addSublayer(ll)
            ll.transform = CATransform3DMakeRotation(CGFloat(i * 30) * CGFloat(rad), 0, 0, 1)
            
            blocks.append(ll)
        }
        progressStep = CGFloat(1) / CGFloat(11)
    }
    
    public override func layoutSubviews() {
        let w = (blockHeight * 3.3 + hollowR) * 2
        let size = CGSize(width: w, height: w)
        self.frame = CGRect(origin: self.frame.origin, size: size)
    }
    
    public func start(){
        for b in blocks {
            b.isHidden = false
            b.opacity = 1
        }
        if spinAngle != -1 {
            let s = spinAngle % 90
            spinTargetAngle = spinAngle - s + 180
        }
        task = {
            [weak self] in
            if let wself = self {
                if wself.step == 0 {
                    if wself.displayLink.duration > 0 {
                        wself.step = Int(0.1 / wself.displayLink.duration)
                    }
                }
                else{
                    wself.activiyAnim()
                }
            }
        }
        
        self.displayLink.isPaused = false
    }
    
    fileprivate func activiyAnim(){
        
        if spinAngle != -1 {
            if spinAngle + 70 == spinTargetAngle {
                CATransaction.begin()
                CATransaction.setAnimationDuration(Double(spinTargetAngle - spinAngle) * 0.031)
                let timing = CAMediaTimingFunction.init(controlPoints: 0, 0.04, 0.2, 1)
                CATransaction.setAnimationTimingFunction(timing)
                
                parentLayer.transform = CATransform3DMakeRotation(CGFloat(spinTargetAngle + startPosition.rawValue) * CGFloat(rad), 0, 0, 1)
                CATransaction.commit()
                spinAngle = -1
            }
            else{
                spinAngle += 2
                CATransaction.begin()
                CATransaction.setDisableActions(true)
                parentLayer.transform = CATransform3DMakeRotation(CGFloat(spinAngle + startPosition.rawValue) * CGFloat(rad), 0, 0, 1)
                CATransaction.commit()
            }
        }
        
        self.curStep += 1
        if self.curStep > self.step {
            self.curStep = 0
            self.cur += 1
            CATransaction.begin()
            CATransaction.setDisableActions(true)
            for i in 0 ..< self.blocks.count {
                let c = (self.cur + i) % self.blocks.count
                self.blocks[self.blocks.count - i - 1].opacity = Float(1 - CGFloat(c) / CGFloat(self.blocks.count))
            }
            CATransaction.commit()
        }
        if self.cur > 99999 {
            self.cur = 0
        }
    }
    
    public func pause(){
        self.displayLink.isPaused = true
    }
    
    public func stop(){
        self.displayLink.isPaused = true
        for l in blocks {
            l.opacity = 1
            l.isHidden = false
        }
        curStep = 0
        cur = 0
        step = 0
        spinAngle = -1
    }
    
    public func hide(){
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        stop()
        for l in blocks {
            l.opacity = 0
            l.isHidden = false
        }
        CATransaction.commit()
    }
    
    public func finish(){
        
        let a0 = CABasicAnimation(keyPath: "transform.scale.x")
        a0.fromValue = 1
        a0.toValue = 0
        
        let a1 = CABasicAnimation(keyPath: "transform.scale.y")
        a1.fromValue = 1
        a1.toValue = 0
        
        let anims = CAAnimationGroup()
        anims.delegate = self
        anims.isRemovedOnCompletion = false
        anims.fillMode = kCAFillModeForwards
        anims.animations = [a0, a1]
        parentLayer.add(anims, forKey: nil)
    }
    
    // MARK: 旋转
    public func spin(){
        if spinAngle == -1 {
            for b in blocks {
                b.isHidden = false
                b.opacity = 1
            }
            spinAngle = 0
            task = {
                [weak self] in
                self?.spinAnim()
            }
            displayLink.isPaused = false
        }
    }
    
    fileprivate func spinAnim(){
        
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        
        var op = 1 - CGFloat(spinAngle) / CGFloat(150)
        if op < 0 {
            op = 0
        }
        else{
            spinAngle += 2
            parentLayer.transform = CATransform3DMakeRotation(CGFloat(spinAngle + startPosition.rawValue) * CGFloat(rad), 0, 0, 1)
        }
        
        if self.step == 0 {
            if self.displayLink.duration > 0 {
                self.step = Int(0.1 / self.displayLink.duration)
            }
        }
        else{
            
            self.curStep += 1
            if self.curStep > self.step {
                self.curStep = 0
                self.cur += 1
                for i in 0 ..< self.blocks.count {
                    let c = (self.cur + i) % self.blocks.count
                    self.blocks[self.blocks.count - i - 1].opacity = Float(1 - CGFloat(c) / CGFloat(self.blocks.count)  + op)
                }
            }
        }
        
        CATransaction.commit()
        if self.cur > 99999 {
            self.cur = 0
        }
        
        if op == 0 {
            start()
        }
    }
    
    public func resetProgressZero(){
        task = {
            [weak self] in
            self?.resetP()
        }
        displayLink.isPaused = false
    }
    
    fileprivate func resetP(){
        if step == 0 {
            if displayLink.duration != 0 {
                step = Int(0.2 / displayLink.duration)
            }
        }
        else {
            
            curStep += 1
            if curStep > step {
                self.progress -= 0.1
                if self.progress == 0 {
                    task = nil
                    displayLink.isPaused = true
                    step = 0
                }
                curStep = 0
            }
        }
    }
    
    public var isAnimating: Bool {
        return self.displayLink.isPaused == false
    }
    
    @objc private func displayLinkHandle(){
        task?()
    }
    
    fileprivate func didSetProgress(){
        
        displayLink.isPaused = true
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        let angle = CGFloat(rad) * CGFloat(startPosition.rawValue)
        parentLayer.transform = CATransform3DMakeRotation(angle, 0, 0, 1)
         
        let k = progress / progressStep
        var i = 0
        for b in blocks {
            if i <= Int(floor(k)) {
                b.isHidden = false
            }
            else {
                b.isHidden = true
            }
            b.opacity = 1
            i += 1
        }
        if k < 1 {
            let op = progress / progressStep
            blocks[0].opacity = Float(op)
        }
        CATransaction.commit()
    }
    
    deinit {
        self.displayLink.invalidate()
    }
    
}

extension QSActivityIndicatorView: CAAnimationDelegate {
    public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        hide()
        parentLayer.removeAllAnimations()
    }
}

fileprivate class BlockLayer: CALayer {
    
    var shapeLayer = CAShapeLayer()
    
    override init() {
        super.init()
        self.addSublayer(shapeLayer)
    }
    
    override init(layer: Any) {
        super.init(layer: layer)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
